/**********************************************************************
 * SailmiPIM - modular cross-platform feature Personal Information Manager Tools.
 * Copyright(C) 2006-2014  Georg Rudoy
 *
 * Distributed under the Boost Software License, Version 1.0.
 *(See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
 **********************************************************************/

#include "smschemehandler.h"
#include <QBuffer>
#include <QWebEngineProfile>
#include <QWebEngineUrlRequestJob>
#include <QWebEngineUrlScheme>
#include <QtDebug>
#include <sailmipim/core/icoreproxy.h>
#include <sailmipim/core/ipluginsmanager.h>
#include <sailmipim/browser/iinternalschemehandler.h>

namespace SailmiPIM::Plugin::Browser::WebEngineView
{
	namespace
	{
		auto GetScheme()
		{
            QWebEngineUrlScheme scheme { "sm" };
			scheme.setSyntax(QWebEngineUrlScheme::Syntax::Path);
			scheme.setFlags(QWebEngineUrlScheme::SecureScheme |
					QWebEngineUrlScheme::LocalScheme |
					QWebEngineUrlScheme::LocalAccessAllowed);
			return scheme;
		}
	}

    void SMSchemeHandler::Register(QWebEngineProfile& profile)
	{
        profile.installUrlSchemeHandler(GetScheme().name(), new SMSchemeHandler { &profile });
	}

	namespace
	{
		auto ToJobError(IInternalSchemeHandler::Error error)
		{
			switch(error)
			{
			case IInternalSchemeHandler::Error::Unsupported:
				return QWebEngineUrlRequestJob::NoError;
			case IInternalSchemeHandler::Error::NotFound:
				return QWebEngineUrlRequestJob::UrlNotFound;
			case IInternalSchemeHandler::Error::Denied:
				return QWebEngineUrlRequestJob::RequestDenied;
			}

			return QWebEngineUrlRequestJob::NoError;
		}

		auto ToJobDevice(QObject *context, const IInternalSchemeHandler::ReplyContents& contents)
		{
			return Util::Visit(contents,
					[context](const QByteArray& bytes)
					{
						auto buffer = new QBuffer { context };
						buffer->setData(bytes);
						return static_cast<QIODevice*>(buffer);
					},
					[context](const std::shared_ptr<QIODevice>& device)
					{
						// keep the device alive while the context is alive
						QObject::connect(context,
								&QObject::destroyed,
								context,
								[device] {});
						return device.get();
					});
		}
	}

    void SMSchemeHandler::requestStarted(QWebEngineUrlRequestJob *job)
	{
		const auto& url = job->requestUrl();
		if(url.scheme() != GetScheme().name())
			return;

		const auto& handlers = GetProxyHolder()->GetPluginsManager()->GetAllCastableTo<IInternalSchemeHandler*>();
		if(handlers.isEmpty())
			return;

		const IInternalSchemeHandler::Request req
		{
			.Url_ = job->requestUrl(),
			.Initiator_ = job->initiator(),
		};
		for(const auto& handler : handlers)
		{
			const auto handled = Util::Visit(handler->HandleRequest(req),
					[job](IInternalSchemeHandler::Error error)
					{
						switch(error)
						{
						case IInternalSchemeHandler::Error::Unsupported:
							return false;
						default:
							job->fail(ToJobError(error));
							return true;
						}
					},
					[job](const IInternalSchemeHandler::Reply& reply)
					{
						job->reply(reply.ContentType_, ToJobDevice(job, reply.Contents_));
						return true;
					});

			if(handled)
				break;
		}
	}

    [[maybe_unused]] const auto SMSchemeRegistrar = []
	{
		auto scheme = GetScheme();
		QWebEngineUrlScheme::registerScheme(scheme);
		return scheme;
	}();
}
